package com.tang.config;

import com.baomidou.mybatisplus.entity.GlobalConfiguration;
import com.baomidou.mybatisplus.mapper.LogicSqlInjector;
import com.baomidou.mybatisplus.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.plugins.PerformanceInterceptor;
import com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.plugin.Interceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

/**
 * MyBatis的配置类
 *
 * @author Raye
 * @since 2018年10月7日14:13:39
 */
@Configuration
@AutoConfigureAfter({DynamicDataSourceConfig.class})
@MapperScan(basePackages = "com.tang.modules.*.dao")
public class MybatisPlusConfig {

    /**
     * 这里全部使用mybatis-autoconfigure 已经自动加载的资源。不手动指定
     * 配置文件和mybatis-boot的配置文件同步
     * MybatisSqlSessionFactoryBean非原生的类
     * 参考https://blog.csdn.net/u014519194/article/details/53786630
     * @return
     */
    @Bean
    public MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean(@Qualifier("dataSource") DynamicDataSource dataSource) throws Exception{
        MybatisSqlSessionFactoryBean mybatisPlus = new MybatisSqlSessionFactoryBean();
        mybatisPlus.setDataSource(dataSource);
        mybatisPlus.setVfs(SpringBootVFS.class);
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        configuration.setLazyLoadingEnabled(true); // 全局启用或禁用延迟加载。当禁用时，所有关联对象都会即时加载，特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态
        configuration.setMapUnderscoreToCamelCase(true); // 开启自动驼峰命名规则（camel case）映射
        // 二级缓存
        configuration.setCacheEnabled(false);
        configuration.setCallSettersOnNulls(true);
        mybatisPlus.setConfiguration(configuration);
        Interceptor[] plugins = new Interceptor[]{paginationInterceptor()};
        mybatisPlus.setPlugins(plugins);
        mybatisPlus.setTypeAliasesPackage("com.tang.modules.*.entity");
        mybatisPlus.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
        GlobalConfiguration gc = new GlobalConfiguration();
        gc.setIdType(0);
//        可选参数（默认mysql）
//        MYSQL->`mysql`
//        ORACLE->`oracle`
//        DB2->`db2`
//        H2->`h2`
//        HSQL->`hsql`
//        SQLITE->`sqlite`
//        POSTGRE->`postgresql`
//        SQLSERVER2005->`sqlserver2005`
//        SQLSERVER->`sqlserver`
        gc.setDbType("mysql");
        gc.setFieldStrategy(2);
        // 在MP中，我们建议数据库表名采用下划线命名方式，而表字段名采用驼峰命名方式
        // 这么做的原因是为了避免在对应实体类时产生的性能损耗，这样字段不用做映射就能直接和实体类对应。
        // 当然如果项目里不用考虑这点性能损耗，那么你采用下滑线也是没问题的，只需要在生成代码时配置dbColumnUnderline属性就可以。
        // 如果表的字段为大写，则isCapitalMode设置为true
        // gc.setCapitalMode(true);
        gc.setDbColumnUnderline(true);
        gc.setRefresh(true);
        // 逻辑删除全局值
        gc.setLogicDeleteValue("-1");
        // 逻辑未删除全局值
        gc.setLogicNotDeleteValue("0");
        // 逻辑删除的字段需要注解@TableLogic
        // 集成全局注入器为 LogicSqlInjector
        // 会在mp自带查询和更新方法的sql后面，追加『逻辑删除字段』=『LogicNotDeleteValue默认值』
        // 删除方法: deleteById()和其他delete方法, 底层SQL调用的是update tbl_xxx set 『逻辑删除字段』=『logicDeleteValue默认值』
        LogicSqlInjector logicSqlInjector = new LogicSqlInjector();
        gc.setSqlInjector(logicSqlInjector);
        return mybatisPlus;
    }


    /**
     *   mybatis-plus分页插件
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor page = new PaginationInterceptor();
        page.setDialectType("mysql");
        // page.setSqlParser(); // 自定义解析类、可以没有
        // page.setLocalPage(true) // 默认 false 改为 true 开启了pageHeper 支持、可以没有
//        List<ISqlParser> sqlParserList = new ArrayList<>();
//        TenantSqlParser tenantSqlParser = new TenantSqlParser();
//        tenantSqlParser.setTenantHandler(new TenantHandler() {
//            @Override
//            public Expression getTenantId() {
//                return new LongValue(1L);
//            }
//
//            @Override
//            public String getTenantIdColumn() {
//                return "tenant_id";
//            }
//
//            @Override
//            public boolean doTableFilter(String tableName) {
//                // 这里可以判断是否过滤表
//            /*
//            if ("user".equals(tableName)) {
//                return true;
//            }*/
//                return false;
//            }
//        });
//        sqlParserList.add(tenantSqlParser);
//        paginationInterceptor.setSqlParserList(sqlParserList);
//        paginationInterceptor.setSqlParserFilter(new ISqlParserFilter() {
//            @Override
//            public boolean doFilter(MetaObject metaObject) {
//                MappedStatement ms = PluginUtils.getMappedStatement(metaObject);
//                // 过滤自定义查询此时无租户信息约束【 麻花藤 】出现
//                if ("com.baomidou.springboot.mapper.UserMapper.selectListBySQL".equals(ms.getId())) {
//                    return true;
//                }
//                return false;
//            }
//        });
        return page;
    }

    /**
     * SQL执行效率插件
     * SQL执行性能分析，开发环境使用，线上不推荐。
     * @return
     */
    @Bean
    @Profile({"dev","test"})// 设置 dev test 环境开启
    public PerformanceInterceptor performanceInterceptor() {
        PerformanceInterceptor page = new PerformanceInterceptor();
        // maxTime 指的是SQL执行最大时长，超过自动停止运行，有助于发现问题
        page.setMaxTime(100);
        // SQL是否格式化 默认false
        page.setFormat(true);
        return page;
    }

    /**
     * 乐观锁插件
     * 当要更新一条记录的时候，希望这条记录没有被别人更新
     * 特别说明： 仅支持int,Integer,long,Long,Date,Timestamp
     * 乐观锁实现方式：
     *        取出记录时，获取当前version
     *        更新时，带上这个version
     *        执行更新时， set version = yourVersion+1 where version = yourVersion
     *        如果version不对，就更新失败
     * 注解实体字段 @Version 必须要！
     *    public class User {
     *             @Version
     *             private Integer version;
     * }
     */
//    @Bean
//    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
//        return new OptimisticLockerInterceptor();
//    }

}
